package com.sl.ms.carriage.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.CarriageDTO;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.domain.enums.EconomicRegionEnum;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.enums.CarriageExceptionEnum;
import com.sl.ms.carriage.handler.CarriageChainHandler;
import com.sl.ms.carriage.mapper.CarriageMapper;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.ms.carriage.utils.CarriageUtils;
import com.sl.transport.common.exception.SLException;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService {


    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Override
    public CarriageDTO saveOrUpdate(CarriageDTO carriageDto) {
        //1.根据条件查询模版


        //3.是否属于经济区互寄模版
        if (ObjectUtil.notEqual(carriageDto.getTemplateType(),CarriageConstant.ECONOMIC_ZONE)){
            CarriageEntity carriageEntity = this.findByTemplateType(carriageDto.getTemplateType());

            //2.判断模版是否存在，如果不存在   直接保存
            if(ObjectUtil.isEmpty(carriageEntity)){
                //自己保存
                CarriageEntity entity = CarriageUtils.toEntity(carriageDto);
                //删除该模版类型的模版
                //删除存储Redis全部列表
                stringRedisTemplate.delete(CARRIAGE_LIST);
                //重新查询
                findAll();

                this.saveOrUpdate(entity);
                return carriageDto;
            }
            //3.1 如果不是经济区，判断id是否为空
            if(ObjectUtil.isEmpty(carriageDto.getId())){
                throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
            }

            //3.2判断当前要修改的ID 和 已存在的模版id是否相等
            if (ObjectUtil.equals(carriageDto.getId(),carriageEntity.getId())){
                    throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
            }

            CarriageEntity entity = CarriageUtils.toEntity(carriageDto);
            //删除该模版类型的模版
            //删除存储Redis全部列表
            stringRedisTemplate.delete(CARRIAGE_LIST);
            //重新查询
            findAll();

            this.saveOrUpdate(entity);
            return carriageDto;

        }



        //4.是经济区互寄模版，判断新的模版经济区 和 已存在的模版经济区id不能重复
        List<CarriageEntity> economicTemplateType = findByEconomicTemplateType();

       List<String> associateCityList = economicTemplateType.stream()
                .map(CarriageEntity::getAssociatedCity)
                .map(associateCityStr -> StrUtil.split(associateCityStr, ','))
               .flatMap(StreamUtil::of)
               .collect(Collectors.toList());

        Collection<String> intersection = CollUtil.intersection(associateCityList, carriageDto.getAssociatedCityList());
        if(CollUtil.isNotEmpty(intersection)){
            throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT);
        }

        CarriageEntity entity = CarriageUtils.toEntity(carriageDto);
        //删除该模版类型的模版
        //删除存储Redis全部列表
        stringRedisTemplate.delete(CARRIAGE_LIST);
        //重新查询
        findAll();

        this.saveOrUpdate(entity);
        return CarriageUtils.toDTO(entity);
    }

    private static final String CARRIAGE_LIST = "carriageList";
    private static final String CARRIAGE_QUERY_EMPTY = "{}";
//    private static String cacheKey = "carriage:all";

    @Override
    public List<CarriageDTO> findAll() {
        stringRedisTemplate.delete(CARRIAGE_LIST);
        //查询redis数据
        String redisList = stringRedisTemplate.opsForValue().get(CARRIAGE_LIST);
        if (ObjectUtil.isNotEmpty(redisList)){
            if(redisList.equals(CARRIAGE_QUERY_EMPTY)){
                throw new SLException(CarriageExceptionEnum.List_Not__Found);
            }
            List<CarriageDTO> list = JSONUtil.toList(redisList, CarriageDTO.class);
            return list;
        }

        LambdaQueryWrapper<CarriageEntity> queryWrapper1 = Wrappers.<CarriageEntity>lambdaQuery().orderByDesc(CarriageEntity::getCreated);
        List<CarriageEntity> list = this.list(queryWrapper1);
        if(ObjectUtil.isNotEmpty(list)){
            List<CarriageDTO> collect = list.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());
            stringRedisTemplate.opsForValue().set(CARRIAGE_LIST, JSONUtil.toJsonStr(collect));
        }else {
            stringRedisTemplate.opsForValue().set(CARRIAGE_LIST, CARRIAGE_QUERY_EMPTY);
        }
        return list.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());

        // 根据key从缓存中获取数据
//        String carriage = stringRedisTemplate.opsForValue().get(cacheKey);
//        //  判断缓存是否存在
//        if (ObjectUtil.isNotEmpty(carriage)) {
//            return JSONUtil.toList(carriage, CarriageDTO.class);
//        }else {
//            // 缓存不存在，查询数据库
//            LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery().orderByDesc(CarriageEntity::getCreated);
//            List<CarriageEntity> list = this.list(queryWrapper);
//            // 将结果写入缓存
//            List<CarriageDTO> collect = list.stream().map(CarriageUtils::toDTO).collect(Collectors.toList());
//            stringRedisTemplate.opsForValue().set(cacheKey, JSONUtil.toJsonStr(collect));

        }




        @Resource
        private CarriageChainHandler carriageChainHandler;

    @Override
    public CarriageDTO compute(WaybillDTO waybillDTO) {
        //1.查询运费模版
//        CarriageEntity carriageEntity = findByTemplateType(waybillDTO);
        CarriageEntity carriageEntity = carriageChainHandler.findCarriage(waybillDTO);
        //2.算出计费重量
      double weight =  computeWeight(waybillDTO,carriageEntity);
        //3.根据公式计算运费  运费 = 首重价格 + 续重价格 * （计费重量 - 1）

        double expense = carriageEntity.getFirstWeight() + carriageEntity.getContinuousWeight() * (weight - 1);
        expense = NumberUtil.round(expense, 1).doubleValue();
        CarriageDTO carriageDTO = CarriageUtils.toDTO(carriageEntity);
        carriageDTO.setExpense(expense);
        return carriageDTO;
    }

    private double computeWeight(WaybillDTO waybillDTO,CarriageEntity carriageEntity) {
        //1.查询体积
        Integer volume = waybillDTO.getVolume();
        if(ObjectUtil.isEmpty(volume)){
            Integer measureLong = waybillDTO.getMeasureLong();
            Integer measureWidth = waybillDTO.getMeasureWidth();
            Integer measureHigh = waybillDTO.getMeasureHigh();
            try {
                volume = measureLong * measureWidth * measureHigh;
            } catch (Exception e) {
                volume = 0;
            }
        }
        //2.体积重量 = 体积/轻抛系数
        BigDecimal volumeWeight = NumberUtil.div(volume, carriageEntity.getLightThrowingCoefficient());
        //3.对比实际重量 和 体积重量 取最大值
        double computeWeight = NumberUtil.max(volumeWeight.doubleValue(), waybillDTO.getWeight());
        //4.按照需求优化小数点
//        不满1kg，按1kg计费；
        if (computeWeight <= 1) {
           return  1;
        }
//        10KG以下：以0.1kg为计重单位，四舍五入保留 1 位小数；
        if (computeWeight <= 10) {
            return NumberUtil.round(computeWeight, 1).doubleValue();
        }

        //        100KG及以上：四舍五入取整；
        if (computeWeight >= 100) {
               return NumberUtil.round(computeWeight, 0).doubleValue();
        }
//        10-100KG：续重以0.5kg为计重单位，不足0.5kg按0.5kg算，四舍五入保留 1 位小数；
        double round = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).doubleValue();
        double sub = computeWeight - round;
        if (sub < 0.5) {
           return NumberUtil.add(round, 0.5);
        }else {
            return NumberUtil.add(round, 1);
        }

    }

    @Resource
    private AreaFeign areaFeign;

    private CarriageEntity findByTemplateType(WaybillDTO waybillDTO){
        //找模版的顺序：同城模版  --> 同省 --> 经济区 --> 跨省


        //1.找同城模版
        Long senderCityId = waybillDTO.getSenderCityId();
        Long receiverCityId = waybillDTO.getReceiverCityId();
        if(ObjectUtil.equals(senderCityId,receiverCityId)){
            CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_CITY);
            if(ObjectUtil.isEmpty(carriageEntity)){
                return carriageEntity;
            }
        }

        //2.找同省
        Long parentId = areaFeign.get(senderCityId).getParentId();
        Long receiverProvinceId = areaFeign.get(receiverCityId).getParentId();
        if(ObjectUtil.equals(parentId,receiverProvinceId)){
            CarriageEntity carriageEntity = findByTemplateType(CarriageConstant.SAME_PROVINCE);
            if(ObjectUtil.isNotEmpty(carriageEntity)){
                return carriageEntity;
            }
        }


        //3.找经济区互寄
       CarriageEntity carriageEntity = findByEconomic(senderCityId,receiverCityId);
        if(ObjectUtil.isNotEmpty(carriageEntity)){
            return carriageEntity;
        }


        //4.找跨省
        carriageEntity = findByTemplateType(CarriageConstant.TRANS_PROVINCE);
        if(ObjectUtil.isNotEmpty(carriageEntity)){
            return carriageEntity;
        }
        throw new SLException(CarriageExceptionEnum.NOT_FOUND);
    }

    private  CarriageEntity findByEconomic(Long senderCityId, Long receiverCityId) {
        //KEY : BTH  VALUE : 对象（code,[]value）
        LinkedHashMap<String, EconomicRegionEnum> enumMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        EconomicRegionEnum economicRegionEnum = null;
        for (EconomicRegionEnum regionEnum : enumMap.values()){
            Long[] value = regionEnum.getValue();
            boolean containsAll = ArrayUtil.containsAll(value, senderCityId, receiverCityId);
            if(containsAll){
                economicRegionEnum = regionEnum;
                break;
            }
        }
        if(ObjectUtil.isNull(economicRegionEnum)){
            return null;
        }
        CarriageEntity carriageEntity = this.getOne(Wrappers.<CarriageEntity>lambdaQuery()
                .eq(CarriageEntity::getTemplateType,CarriageConstant.ECONOMIC_ZONE)
                .like(CarriageEntity::getAssociatedCity,economicRegionEnum.getCode()));
        return carriageEntity;
    }


    public List<CarriageEntity> findByEconomicTemplateType(){
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery().eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE);

        return this.list(queryWrapper);
    }

    @Override
    public CarriageEntity findByTemplateType(Integer templateType) {

        if(ObjectUtil.equals(CarriageConstant.ECONOMIC_ZONE,templateType)){
            throw new SLException(CarriageExceptionEnum.TEMPLATE_TYPE_NOT_SUPPORT);
        }

        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
                .eq(CarriageEntity::getTemplateType, templateType);
        CarriageEntity carriageEntity = this.getOne(queryWrapper);


        return carriageEntity;
    }
}
